package com.accounting.juanjuan.utils;

import com.accounting.juanjuan.entity.AmountData;
import com.accounting.juanjuan.entity.ComputeAccountingData;
import com.accounting.juanjuan.exception.AppException;
import com.accounting.juanjuan.exception.BizException;

import java.math.BigDecimal;
import java.util.*;

/**
 * 会计数据计算工具类
 *
 * @Author xxj
 * @Date 2024/1/26 15:44
 * @Version 1.0
 */
public class ComputeAccountingUtil {
    /**
     * 计算金额列表之和
     */
    public static BigDecimal computeListTotalOnAmount(List<AmountData> amountDataList) {
        Optional<Double> reduce = amountDataList.stream().map(AmountData::getAmount).reduce((acc, n) -> BigDecimal.valueOf(acc).add(BigDecimal.valueOf(n)).doubleValue());
        if(!reduce.isPresent()){
            throw new AppException("数据求和时发生错误！");
        }
        return BigDecimal.valueOf(reduce.get());
    }

    /**
     * 计算两个列表金额
     */
    public static double sumTwoList(List<AmountData> positiveNumbers, List<AmountData> negativeNumbers) {
        BigDecimal decimal1 = computeListTotalOnAmount(positiveNumbers);
        BigDecimal decimal2 = computeListTotalOnAmount(negativeNumbers);
        return decimal1.add(decimal2).doubleValue();
    }

    /**
     * 去除正数和负数的绝对值相等的
     */
    public static void removePositiveNumEqualNegativeNum(List<AmountData> positiveNumbers, List<AmountData> negativeNumbers) {
        // 使用迭代器遍历正数列表
        Iterator<AmountData> positiveIterator = positiveNumbers.iterator();
        while (positiveIterator.hasNext()) {
            AmountData positiveNum = positiveIterator.next();

            // 使用迭代器遍历负数列表
            Iterator<AmountData> negativeIterator = negativeNumbers.iterator();
            while (negativeIterator.hasNext()) {
                AmountData negativeNum = negativeIterator.next();

                if (BigDecimal.valueOf(positiveNum.getAmount()).add(BigDecimal.valueOf(negativeNum.getAmount())).doubleValue() == 0) {
                    // 使用迭代器安全地移除元素
                    positiveIterator.remove();
                    negativeIterator.remove();

                    break; // 终止内部循环，已找到匹配组合
                }
            }
        }
    }

    /**
     * 递归生成列表不同组合值
     */
    private static void generateCombinationsHelper(List<List<AmountData>> result, List<AmountData> currentCombination, List<AmountData> numbers, int n, int start) {
        if (currentCombination.size() == n) {
            result.add(new ArrayList<>(currentCombination));
            return;
        }

        for (int i = start; i < numbers.size(); i++) {
            currentCombination.add(numbers.get(i));
            generateCombinationsHelper(result, currentCombination, numbers, n, i + 1);
            currentCombination.remove(currentCombination.size() - 1);
        }
    }

    /**
     * 递归生成列表不同组合值
     */
    private static List<List<AmountData>> generateNCombinations(List<AmountData> numbers, int n) {
        List<List<AmountData>> result = new ArrayList<>();
        List<AmountData> currentCombination = new ArrayList<>();
        generateCombinationsHelper(result, currentCombination, numbers, n, 0);
        return result;
    }

    /**
     * 多种组合求和等于0
     */
    public static boolean combiningAndSummingToZero(List<AmountData> target, List<AmountData> to, List<AmountData> result, int count) {
        List<List<AmountData>> differentCombinations = generateNCombinations(target, count);
        for (List<AmountData> amountDataList : differentCombinations) {
            BigDecimal decimal1 = computeListTotalOnAmount(amountDataList);
            for (AmountData amountData : to) {
                if (decimal1.add(BigDecimal.valueOf(amountData.getAmount())).doubleValue() == 0) {
                    result.addAll(amountDataList);
                    result.add(amountData);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 去除，多正对一负或者多负对一正金额相同的
     */
    public static void removePositiveNumEqualNegativeNumByMoreOne(List<AmountData> positiveNumbers, List<AmountData> negativeNumbers) {
        List<AmountData> amountDataList = new ArrayList<>();
        //从2对1开始，依次是3对1....
        for (int i = 2; i <= Math.max(positiveNumbers.size(), negativeNumbers.size()); i++) {
            if (positiveNumbers.size() >= i) {
                //正数
                boolean isZero = combiningAndSummingToZero(positiveNumbers, negativeNumbers, amountDataList, i);
                //一旦找到一种组合成功，立马清除，再重新组合，防止一个数重复使用，直到没有组合成功的，进入下一次循环
                if (isZero) {
                    for (AmountData amountData : amountDataList) {
                        positiveNumbers.removeIf(infp1 -> infp1.equals(amountData));
                        negativeNumbers.removeIf(infp1 -> infp1.equals(amountData));
                    }
                    i -= 1;
                    amountDataList.clear();
                    continue;
                }
            }
            if (negativeNumbers.size() >= i) {
                //负数
                boolean isZero = combiningAndSummingToZero(negativeNumbers, positiveNumbers, amountDataList, i);
                if (isZero) {
                    for (AmountData amountData : amountDataList) {
                        positiveNumbers.removeIf(infp1 -> infp1.equals(amountData));
                        negativeNumbers.removeIf(infp1 -> infp1.equals(amountData));
                    }
                    i -= 1;
                    amountDataList.clear();
                }
            }
        }
    }

    /**
     * 合并列表并排序
     */
    public static List<AmountData> mergeOrderListOnXh(List<AmountData> positiveNumbers, List<AmountData> negativeNumbers) {
        List<AmountData> totalList = new ArrayList<>(positiveNumbers);
        totalList.addAll(negativeNumbers);
        totalList.sort(Comparator.comparingInt(AmountData::getXh));
        return totalList;
    }

    /**
     * 去除相加等于0，入口 -- 中力度（一对一，多对一）
     */
    public static ComputeAccountingData mediumStrength(List<AmountData> positiveNumbers, List<AmountData> negativeNumbers) {
        ComputeAccountingData computeAccountingData = new ComputeAccountingData();
        //初始值相差金额数
        double initAmountTotal = sumTwoList(positiveNumbers, negativeNumbers);
        if (initAmountTotal == 0) {
            throw new BizException("经计算，总金额想和等于0，无需处理！");
        }
        computeAccountingData.setInitAmountTotal(initAmountTotal);
        //去除，正金额和负金额相同的
        removePositiveNumEqualNegativeNum(positiveNumbers, negativeNumbers);
        //去除，多正对一负或者多负对一正金额相同的
        removePositiveNumEqualNegativeNumByMoreOne(positiveNumbers, negativeNumbers);
        //计算最终结果相差金额数
        double resultAmountTotal = sumTwoList(positiveNumbers, negativeNumbers);
        computeAccountingData.setResultAmountTotal(resultAmountTotal);
        //比较结果，进行判断数据是否处理出现异常
        computeAccountingData.setEqualStatue(BigDecimal.valueOf(initAmountTotal).equals(BigDecimal.valueOf(resultAmountTotal)));
        //对结果进行合并，按照序号排序
        computeAccountingData.setAmountDataList(mergeOrderListOnXh(positiveNumbers, negativeNumbers));
        return computeAccountingData;
    }

    /**
     * 去除相加等于0，入口 -- 小力度（只有一对一）
     */
    public static ComputeAccountingData smallForce(List<AmountData> positiveNumbers, List<AmountData> negativeNumbers) {
        ComputeAccountingData computeAccountingData = new ComputeAccountingData();
        //初始值相差金额数
        double initAmountTotal = sumTwoList(positiveNumbers, negativeNumbers);
        if (initAmountTotal == 0) {
            throw new BizException("经计算，总金额想和等于0，无需处理！");
        }
        computeAccountingData.setInitAmountTotal(initAmountTotal);
        //去除，正金额和负金额相同的
        removePositiveNumEqualNegativeNum(positiveNumbers, negativeNumbers);
        //计算最终结果相差金额数
        double resultAmountTotal = sumTwoList(positiveNumbers, negativeNumbers);
        computeAccountingData.setResultAmountTotal(resultAmountTotal);
        //比较结果，进行判断数据是否处理出现异常
        computeAccountingData.setEqualStatue(BigDecimal.valueOf(initAmountTotal).equals(BigDecimal.valueOf(resultAmountTotal)));
        //对结果进行合并，按照序号排序
        computeAccountingData.setAmountDataList(mergeOrderListOnXh(positiveNumbers, negativeNumbers));
        return computeAccountingData;
    }
}
